# 首先导入keras构建模型的必备工具包
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Embedding
from keras.layers import GlobalAveragePooling1D
from keras.models import load_model
import matplotlib.pyplot as plt

# batch_size是每次进行参数更新的样本数量
batch_size = 32
# epochs将全部数据遍历训练的次数
epochs = 40
# 定义词嵌入维度为50
embedding_dims = 50

def model_build(maxlen, new_max_features):
    """该函数用于模型结构构建"""

    # 在函数中，首先初始化一个序列模型对象
    model = Sequential()

    # 然后首层使用Embedding层进行词向量映射
    model.add(Embedding(new_max_features,
                        embedding_dims,
                        input_length=maxlen))

    # 然后用构建全局平均池化层，减少模型参数，防止过拟合
    model.add(GlobalAveragePooling1D())

    # 最后构建全连接层 + sigmoid层来进行分类.
    model.add(Dense(1, activation='sigmoid'))
    return model


def model_compile(model):
    """用于选取模型的损失函数和优化方法"""
    # 使用model自带的compile方法，选择预定义好的二分类交叉熵损失函数，Adam优化方法，以及准确率评估指标.
    model.compile(loss='binary_crossentropy',
                  optimizer='adam',
                  metrics=['accuracy'])
    return model


def model_fit(model, x_train, y_train):
    """用于模型训练"""
    history = model.fit(x_train,
                        y_train,
                        batch_size=batch_size,
                        epochs=epochs,
                        # validation_split表示将全部训练数据的多少划分为验证集.
                        validation_split=0.1)
    return history


def plot_loss_acc(history, acc_png_path, loss_png_path):
    """用于绘制模型的损失和acc对照曲线, 以模型训练历史为参数"""
    # 首先获得模型训练历史字典，
    # 形如{'val_loss': [0.8132099324259264, ..., 0.8765081824927494],
    #    'val_acc': [0.029094827586206896,...,0.13038793103448276],
    #     'loss': [0.6650978644232184,..., 0.5267722122513928],
    #     'acc': [0.5803400383141762, ...,0.8469827586206896]}
    history_dict = history.history

    # 取出需要的的各个key对应的value，准备作为纵坐标
    acc = history_dict['acc']
    val_acc = history_dict['val_acc']
    loss = history_dict['loss']
    val_loss = history_dict['val_loss']

    # 取epochs的递增列表作为横坐标
    epochs = range(1, len(acc) + 1)

    # 绘制训练准确率的点图
    plt.plot(epochs, acc, 'bo', label='Training acc')
    # 绘制验证准确率的线图
    plt.plot(epochs, val_acc, 'b', label='Validation acc')
    # 增加标题
    plt.title('Training and validation accuracy')
    # 增加横坐标名字
    plt.xlabel('Epochs')
    # 增加纵坐标名字
    plt.ylabel('Accuracy')
    # 将上面的图放在一块画板中
    plt.legend()
    # 保存图片
    plt.savefig(acc_png_path)

    # 清空面板
    plt.clf()
    # 绘制训练损失的点图
    plt.plot(epochs, loss, 'bo', label='Training loss')
    # 绘制验证损失的线图
    plt.plot(epochs, val_loss, 'b', label='Validation loss')
    # 添加标题
    plt.title('Training and validation loss')
    # 添加横坐标名字
    plt.xlabel('Epochs')
    # 添加纵坐标名字
    plt.ylabel('Loss')
    # 把两张图放在一起
    plt.legend()
    # 保存图片
    plt.savefig(loss_png_path)


def model_save(save_path, model):
    """模型保存函数"""
    # 使用model.save对模型进行保存.
    model.save(save_path)
    return

def model_load(save_path, sample):
    """模型加载与预测函数"""
    # 使用load_model方法进行加载
    model = load_model(save_path)
    # 使用predict方法进行预测
    result = model.predict(sample)
    return result